package com.liao.badminton.filter;

import com.liao.badminton.code.ProjectException;
import com.liao.badminton.code.RespCodeMessage;
import com.liao.badminton.code.util.WebUtil;
import com.liao.badminton.config.IgnoreUrlsConfig;
import com.liao.badminton.context.BaseContextHandler;
import com.liao.badminton.redis.RedisEnum;
import com.liao.badminton.redis.RedisUtil;
import com.liao.badminton.code.util.JwtTokenUtils;
import com.liao.badminton.vo.SystemUsersVo;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.log4j.Log4j2;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Log4j2
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
    private IgnoreUrlsConfig ignoreUrlsConfig;

    public JWTAuthorizationFilter(AuthenticationManager authenticationManager, IgnoreUrlsConfig ignoreUrlsConfig) {
        super(authenticationManager);
        this.ignoreUrlsConfig = ignoreUrlsConfig;
    }


    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws IOException, ServletException {

        String tokenHeader = request.getHeader(JwtTokenUtils.TOKEN_HEADER);
        BaseContextHandler.setIp(WebUtil.getClientIp(request));
        BaseContextHandler.setPort(WebUtil.getClientPort(request));
        String servletPath = request.getServletPath();
        BaseContextHandler.setOption(servletPath);
//        Collection<ConfigAttribute> attributes = (VirtualFilterChain)chain.obtainSecurityMetadataSource().getAttributes(fi);
//        this.accessDecisionManager.decide(authenticated, object, attributes);
        if (shouldSkip(servletPath)) { //可以匿名访问
            //白名单,登录了也可以解释啊，有就放，没有就不会呗
            if (tokenHeader != null && tokenHeader.startsWith(JwtTokenUtils.TOKEN_PREFIX)) { // 要登录的请求
                try {
                    UsernamePasswordAuthenticationToken authentication = getAuthentication(request, tokenHeader);
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                } catch (Exception e) {

                }
            }
        } else if (tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtils.TOKEN_PREFIX)) { //要登录的请求，//  如果请求头中没有Authorization信息则直接放行了
            log.info("请先去登录啊");
            ProjectException.newException(RespCodeMessage.SECURITY_ACCOUNT_UN_LOGIN);
        } else if (tokenHeader != null && tokenHeader.startsWith(JwtTokenUtils.TOKEN_PREFIX)) { //如果请求头中有token，则进行解析，并且设置认证信息
            SecurityContextHolder.getContext().setAuthentication(getAuthentication(request, tokenHeader));
        } else {
            ProjectException.newException(RespCodeMessage.SECURITY_TOKEN_FORMAT, "请以 bearer 开头");
        }
        super.doFilterInternal(request, response, chain);
    }


    // 这里从token中获取用户信息并新建一个token
    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request, String tokenHeader) {
        String token = tokenHeader.replace(JwtTokenUtils.TOKEN_PREFIX, "");
        String tty = request.getHeader(BaseContextHandler.HEADER_TTY);
        try {
            String username = JwtTokenUtils.getUsername(token);
            if (username != null) {
                Object redisDate = RedisUtil.toGetDate(RedisEnum.USER_LOGIN_NAME, username);
                if (redisDate != null && (Boolean) redisDate) {
                    RedisUtil.toFlushExpire(RedisEnum.USER_LOGIN_NAME, username);
                    UserDetails userInfo = JwtTokenUtils.getUserInfo(token);
                    RedisUtil.toFlushExpire(RedisEnum.badminton_user_info_name, username);
                    SystemUsersVo userDetail = (SystemUsersVo) RedisUtil.toGetDate(RedisEnum.badminton_user_info_name, username);
                    if (null != userDetail) {
                        BaseContextHandler.setUserID(userDetail.getId());
                        BaseContextHandler.setUserPhone(userDetail.getMobile());
                        BaseContextHandler.setUserName(userDetail.getUsername());
                        BaseContextHandler.setUserType(userDetail.getUserType());
                        BaseContextHandler.set(BaseContextHandler.HEADER_TTY, tty);
                    }
                    return new UsernamePasswordAuthenticationToken(userInfo, null, userInfo.getAuthorities());
                }
            }
            ProjectException.newException(RespCodeMessage.SECURITY_SESSION_IVN, "无效token");
        } catch (ExpiredJwtException e) {
            ProjectException.newException(RespCodeMessage.SECURITY_CREDENTIAL_EXPIRE, "token过期异常");
        } catch (Exception e) {
            log.error("token异常", e);
            ProjectException.newException(RespCodeMessage.SECURITY_SESSION_IVN, "token异常");
        }
        return null;
    }


    /***
     * 不需要登录的请求： 白名单
     * @param currentUrl
     * @return
     */
    private boolean shouldSkip(String currentUrl) {
        //路径匹配器(简介SpringMvc拦截器的匹配器)
        //比如/oauth/** 可以匹配/oauth/token    /oauth/check_token等
        PathMatcher pathMatcher = new AntPathMatcher();

        for (String skipPath : ignoreUrlsConfig.getUrls()) {
            if (pathMatcher.match(skipPath, currentUrl)) {
                return true;
            }
        }
        return false;
    }
}


